package com.clj.fastble.utils;

import java.util.Locale;

/**
 * HexUtil
 */
public class HexUtil {
    private static final char[] DIGITS_LOWER = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    private static final char[] DIGITS_UPPER = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    };

    /**
     * encodeHex
     * @param data data
     * @return char
     */
    public static char[] encodeHex(byte[] data) {
        return encodeHex(data, true);
    }

    /**
     * encodeHex
     * @param data data
     * @param toLowerCase toLowerCase
     * @return char
     */
    public static char[] encodeHex(byte[] data, boolean toLowerCase) {
        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    }

    /**
     * encodeHex
     * @param data data
     * @param toDigits toDigits
     * @return char[]
     */
    protected static char[] encodeHex(byte[] data, char[] toDigits) {
        if (data == null) {
            return null;
        }
        int len = data.length;
        char[] out = new char[len << 1];
        for (int i = 0, j = 0; i < len; i++) {
            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
            out[j++] = toDigits[0x0F & data[i]];
        }
        return out;
    }

    /**
     * encodeHexStr
     * @param data data
     * @return String
     */
    public static String encodeHexStr(byte[] data) {
        return encodeHexStr(data, true);
    }

    /**
     * encodeHexStr
     * @param data data
     * @param toLowerCase toLowerCase
     * @return String
     */
    public static String encodeHexStr(byte[] data, boolean toLowerCase) {
        return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    }

    /**
     * encodeHexStr
     * @param data data
     * @param toDigits toDigits
     * @return String
     */
    protected static String encodeHexStr(byte[] data, char[] toDigits) {
        return new String(encodeHex(data, toDigits));
    }

    /**
     * formatHexString
     * @param data data
     * @return String
     */
    public static String formatHexString(byte[] data) {
        return formatHexString(data, false);
    }

    /**
     * formatHexString
     * @param data data
     * @param addSpace addSpace
     * @return String
     */
    public static String formatHexString(byte[] data, boolean addSpace) {
        if (data == null || data.length < 1){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            String hex = Integer.toHexString(data[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex);
            if (addSpace){
                sb.append(" ");
            }
        }
        return sb.toString().trim();
    }

    /**
     * decodeHex
     * @param data data
     * @return byte[]
     */
    public static byte[] decodeHex(char[] data) {
        int len = data.length;

        if ((len & 0x01) != 0) {
            throw new RuntimeException("Odd number of characters.");
        }

        byte[] out = new byte[len >> 1];

        // two characters form the hex value.
        for (int i = 0, j = 0; j < len; i++) {
            int fl = toDigit(data[j], j) << 4;
            j++;
            fl = fl | toDigit(data[j], j);
            j++;
            out[i] = (byte) (fl & 0xFF);
        }

        return out;
    }

    /**
     * toDigit
     * @param ch ch
     * @param index index
     * @return int
     */
    protected static int toDigit(char ch, int index) {
        int digit = Character.digit(ch, 16);
        if (digit == -1) {
            throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
        }
        return digit;
    }

    /**
     * hexStringToBytes
     * @param hexString hexString
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.trim();
        hexString = hexString.toUpperCase(Locale.ROOT);
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] dy = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            dy[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return dy;
    }

    /**
     * charToByte
     * @param ch c
     * @return byte
     */
    public static byte charToByte(char ch) {
        return (byte) "0123456789ABCDEF".indexOf(ch);
    }

    /**
     * extractData
     * @param data data
     * @param position position
     * @return String
     */
    public static String extractData(byte[] data, int position) {
        return HexUtil.formatHexString(new byte[] {data[position]});
    }
}
